﻿package solver {
	import flash.display.MovieClip;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	import hanoi.Hanoi;
	import hanoi.History;
	import hanoi.Move;
	import hanoi.Tower;
	
	/**
	 * Document Class para el solucionador del juego de torres de hanoi.
	 */
	public final class HanoiSolver extends MovieClip {
		/**
		 * Constructor.
		 */
		public function HanoiSolver() {
			super();
			var game:Hanoi = new Hanoi(10);
			var history:History = new History();
			history.recordGame(game);
			solve(game);
			replay(history);
		}
		
		/**
		 * Resuelve el juego.
		 * @param game El juego a resolver.
		 */
		private function solve(game:Hanoi):void {
			moveDiscs(game.size, game.firstTower, game.thirdTower, game.secondTower);
		}
		
		/**
		 * Mueve cierta cantidad de discos de una torre a otra.
		 * @param quantity La cantidad de discos a mover.
		 * @param from La torre de origen.
		 * @param to La torre de destino.
		 * @param middle La torre restante.
		 */
		private function moveDiscs(quantity:uint, from:Tower, to:Tower, middle:Tower):void {
			if (quantity == 1) {
				// Caso Base
				from.moveDiscTo(to);
			} else {
				// Caso Recursivo
				moveDiscs(quantity - 1, from, middle, to);
				from.moveDiscTo(to);
				moveDiscs(quantity - 1, middle, to, from);
			}
		}
		
		/**
		 * Próxima movida a ejecutar en la vista.
		 */
		private var move:Move;
		
		/**
		 * Inicia la repetición de la solución obtenida.
		 * @param history Historial de movidas.
		 */
		private function replay(history:History):void {
			move = history.first;
			var timer:Timer = new Timer(HanoiViewer.MOVE_DURATION * 1000);
			timer.addEventListener(TimerEvent.TIMER, replayStep);
			timer.start();
		}
		
		/**
		 * Paso en la visualización de la solución.
		 * @param event Evento del timer para la visualización.
		 */
		private function replayStep(event:TimerEvent):void {
			if (move == null) return;
			move.applyTo(HanoiViewer.instance.game);
			move = move.next;
		}
	}
}
